#pragma once

/// <summary>
/// EffectInstanced: Shader implements standard lighting, texturing, alpha
/// transparency, texture animation, and fog. Up to three directional lights can be used.
/// Per-object world matrix and colour are streamed in via hardware instancing.
/// </summary>

/**	Author: Dane Bulat
  * Created: 06/2013
  * Last Modified: 30/06/2013
  * Version: v1.0
  */

#include "common\d3dx11effect.h"
#include "common\LightHelper.h"		// Material and DirectionalLight.
#include "common\d3dUtil.h"			// Release macros.
#include "ShaderUtil.h"				// Compiling shader programs.

#include <D3D11.h>
#include <xnamath.h>
#include <string>

class InstancedShader
{
private:
//==============================
// Constructor
//==============================
	InstancedShader();

	static InstancedShader* m_pEffect;				///< Pointer to singleton shader.
	static bool				m_instanceFlag;			///< Instance flag.

	ID3DX11Effect*			m_Effect;				///< Pointer to compiled effect.
	ID3D11InputLayout*		m_InputLayout;			///< Pointer to shader input layout.

public:
//==============================
// Destructor
//==============================
	~InstancedShader();

//==============================
// Public Interface Methods
//==============================

	/**
	  * GetInstance
	  *
      * Getter to the single instance of this class.
	  */
	static InstancedShader* GetInstance();

	/**
	  * Initialise
	  *
      * Initialise method that compiles the .hlsl file, binds the C++ variables, and create the input layout.
	  * @param Pointer to D3D Device
	  */
	void Initialise(ID3D11Device* device);

	/**
	  * Shutdown
	  *
      * Releases the necessary COM objects and dynamic memory allocations.
	  */
	void Shutdown();

	/**
	  * GetInputLayout
	  *
      * Getter that returns a pointer to the shader input layout.
	  */
	ID3D11InputLayout* GetInputLayout() const;

	/**
	  * SetWorldViewProj
	  *
      * Setter to set the shader world-view-projection matrix.
	  * @param World-view-projection matrix.
	  */
	void SetWorldViewProj(CXMMATRIX);

	/**
	  * SetViewProj
	  *
      * Setter to set the shader view-projection matrix.
	  * @param Vview-projection matrix.
	  */
	void SetViewProj(CXMMATRIX M);

	/**
	  * SetTexTransform
	  *
      * Setter to set the shader texture transform matrix.
	  * @param Texture transform matrix.
	  */
	void SetTexTransform(CXMMATRIX);

	/**
	  * SetEyePosW
	  *
      * Setter to set the shader eye position.
	  * @param Vector representing camera eye position.
	  */
	void SetEyePosW(const XMFLOAT3& v);

	/**
	  * SetFogColor
	  *
      * Setter to set the shader fog colour.
	  * @param Vector representing the fog colour.
	  */
	void SetFogColor(const XMVECTOR v);

	/**
	  * SetFogStart
	  *
      * Setter to set the starting fog distance relative to the camera.
	  * @param Float representing the starting fog distance from the camera.
	  */
	void SetFogStart(float f);

	/**
	  * SetFogRange
	  *
      * Setter to set the range of the fog.
	  * @param Float representing the range value of the fog.
	  */
	void SetFogRange(float f);

	/**
	  * SetDirLights
	  *
      * Setter to set the shader directional lights.
	  * @param Pointer to a maximum of three directional lights.
	  */
	void SetDirLights(const DirectionalLight* lights);

	/**
	  * SetMaterial
	  *
      * Setter to set the shader material.
	  * @param Material that the shader will use to render geometry.
	  */
	void SetMaterial(const Material& mat);

	/**
	  * SetDiffuseMap
	  *
      * Setter to set the shader diffuse texture.
	  * @param ShaderResourceView that the shader will use to texture geometry.
	  */
	void SetDiffuseMap(ID3D11ShaderResourceView* tex);

private:
//==============================
// Private Methods
//==============================

	/**
	  * BindVariables
	  *
      * Binds the class C++ variables to the shader variables stored on the GPU.
	  */
	void BindVariables();

	/**
	  * CreateInputLayout
	  *
      * Creates the vertex input layout based on the vertex shader signature.
	  * @param Pointer to D3D Device
	  */
	void CreateInputLayout(ID3D11Device* device);

public:
	// Techniques: Lighting
	ID3DX11EffectTechnique* Light1Tech;						///< Technique utilising 1 light.
	ID3DX11EffectTechnique* Light2Tech;						///< Technique utilising 2 lights.
	ID3DX11EffectTechnique* Light3Tech;						///< Technique utilising 3 lights.

	// Techniques: Lighting and Texturing	
	ID3DX11EffectTechnique* Light0TexTech;					///< Technique utilising 0 lights and textures.
	ID3DX11EffectTechnique* Light1TexTech;					///< Technique utilising 1 light and textures.
	ID3DX11EffectTechnique* Light2TexTech;					///< Technique utilising 2 lights and textures.
	ID3DX11EffectTechnique* Light3TexTech;					///< Technique utilising 3 lights and textures.

	// Techniques: Lighting, Texturing and Alpha.
	ID3DX11EffectTechnique* Light0TexAlphaClipTech;			///< Technique utilising 0 lights, textures, and alpha clipping.
	ID3DX11EffectTechnique* Light1TexAlphaClipTech;			///< Technique utilising 1 light, textures, and alpha clipping.
	ID3DX11EffectTechnique* Light2TexAlphaClipTech;			///< Technique utilising 2 lights, textures, and alpha clipping.
	ID3DX11EffectTechnique* Light3TexAlphaClipTech;			///< Technique utilising 3 lights, textures, and alpha clipping.

	// Techniques: Lighting and Fog.
	ID3DX11EffectTechnique* Light1FogTech;					///< Technique utilising 1 light and fog.
	ID3DX11EffectTechnique* Light2FogTech;					///< Technique utilising 2 lights and fog.
	ID3DX11EffectTechnique* Light3FogTech;					///< Technique utilising 3 lights and fog.

	// Techniques: Lighting, Texturing and Fog.
	ID3DX11EffectTechnique* Light0TexFogTech;				///< Technique utilising 0 lights, textures and fog.
	ID3DX11EffectTechnique* Light1TexFogTech;				///< Technique utilising 1 light, textures and fog.
	ID3DX11EffectTechnique* Light2TexFogTech;				///< Technique utilising 2 lights, textures and fog.
	ID3DX11EffectTechnique* Light3TexFogTech;				///< Technique utilising 3 lights, textures and fog.

	// Techniques: Lighting, Texturing, Alpha, and Fog.
	ID3DX11EffectTechnique* Light0TexAlphaClipFogTech;		///< Technique utilising 0 lights, textures, alpha clipping and fog.
	ID3DX11EffectTechnique* Light1TexAlphaClipFogTech;		///< Technique utilising 1 light, textures, alpha clipping and fog.
	ID3DX11EffectTechnique* Light2TexAlphaClipFogTech;		///< Technique utilising 2 lights, textures, alpha clipping and fog.
	ID3DX11EffectTechnique* Light3TexAlphaClipFogTech;		///< Technique utilising 3 lights, textures, alpha clipping and fog.

	// Remaining Effect Variables.
	ID3DX11EffectMatrixVariable* WorldViewProj;				///< Shader world-view-projection matrix.
	ID3DX11EffectMatrixVariable* World;						///< Shader world matrix.
	ID3DX11EffectMatrixVariable* WorldInvTranspose;			///< Shader world-inverse-transpose-matrix.
	ID3DX11EffectMatrixVariable* ViewProj;					///< Shader view-projection matrix.
	ID3DX11EffectMatrixVariable* TexTransform;				///< Shader texture transform matrix.
	ID3DX11EffectVectorVariable* EyePosW;					///< Shader eye position vector.

	ID3DX11EffectVectorVariable* FogColor;					///< Shader fog colour.
	ID3DX11EffectScalarVariable* FogStart;					///< Shader fog starting distance relative to the camera.
	ID3DX11EffectScalarVariable* FogRange;					///< Shader fog range.

	ID3DX11EffectVariable* DirLights;						///< Shader directional lights.
	ID3DX11EffectVariable* Mat;								///< Shader material.

	ID3DX11EffectShaderResourceVariable* DiffuseMap;		///< Shader diffuse texture.

	struct Vertex // Vertex structure for Basic.fx
	{
		Vertex() : Pos(0.0f, 0.0f, 0.0f), Normal(0.0f, 0.0f, 0.0f), Tex(0.0f, 0.0f) {}

		Vertex(const XMFLOAT3& p, const XMFLOAT3& n, const XMFLOAT2& uv)
			: Pos(p), Normal(n), Tex(uv) {}

		Vertex(float px, float py, float pz, float nx, float ny, float nz, float u, float v)
			: Pos(px, py, pz), Normal(nx, ny, nz), Tex(u, v) {}

		XMFLOAT3 Pos;
		XMFLOAT3 Normal;
		XMFLOAT2 Tex;
	};
};